package com.cary.camerax.demo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraX;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.extensions.AutoPreviewExtender;
import androidx.camera.extensions.BeautyPreviewExtender;
import androidx.camera.extensions.BokehPreviewExtender;
import androidx.camera.extensions.ExtensionsManager;
import androidx.camera.extensions.HdrPreviewExtender;
import androidx.camera.extensions.NightPreviewExtender;
import androidx.camera.view.CameraView;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.ExifInterface;
import android.media.Image;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Rational;
import android.util.Size;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.ResourceUtils;
import com.cary.camerax.demo.utils.ResourceUtil;
import com.cary.camerax.demo.view.CameraXCustomPreviewView;
import com.cary.camerax.demo.view.FocusImageView;
import com.google.common.util.concurrent.ListenableFuture;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static androidx.camera.extensions.ExtensionsManager.EffectMode.BEAUTY;
import static androidx.camera.extensions.ExtensionsManager.EffectMode.BOKEH;
import static androidx.camera.extensions.ExtensionsManager.EffectMode.HDR;
import static androidx.camera.extensions.ExtensionsManager.EffectMode.NORMAL;

@SuppressLint({"MissingPermission", "NewApi"})
public class CameraXDemoActivity extends AppCompatActivity implements View.OnClickListener {
    private String TAG = CameraXDemoActivity.class.getSimpleName();

    private final int TAKE_PHOTO_SUCCESS = 0x01;
    private final int TAKE_VIDEO_SUCCESS = 0x02;
    private CameraView mCameraView;

    private ImageView ivBack;
    private Button btnZoom;
    private Button btnFocusing;
    private Button btnFlashLamp;
    private Button btnTakePhoto;
    private Button btnBrightness;
    private Button btnFilter;
    private Button btnScene;
    private Button btnCameraDirection;
    private LinearLayout llZoom;
    private TextView tvZoomValue;
    private SeekBar seekBarZoom;
    private ImageView ivOutPutImage;
    private RadioGroup llFlashLamp;
    private RadioButton radioAuto;
    private RadioButton radioOn;
    private RadioButton radioOff;
    private ExecutorService mExecutorService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getSupportActionBar() != null) {
            getSupportActionBar().hide();
        }
        setContentView(R.layout.activity_camera_x_demo);

        initView();
        initListener();
        mCameraView.setFlash(ImageCapture.FLASH_MODE_AUTO);
        mCameraView.bindToLifecycle(this);
        mExecutorService = Executors.newSingleThreadExecutor();

        Log.d(TAG, "maxZoomLevel=" + mCameraView.getMaxZoomRatio() + ", minZoomLevel=" + mCameraView.getMinZoomRatio());
        seekBarZoom.setMax((int) mCameraView.getMaxZoomRatio());
        seekBarZoom.setMin((int) mCameraView.getMinZoomRatio());
        tvZoomValue.setText(mCameraView.getMinZoomRatio() + "X");
        mCameraView.setCameraLensFacing(CameraSelector.LENS_FACING_FRONT);
        boolean isZoomSupported= mCameraView.isZoomSupported();
        Log.d(TAG, "isZoomSupported=" + isZoomSupported);
        Log.d(TAG, "isZoomSupported=" + isZoomSupported);
        boolean isExtensionAvailable = ExtensionsManager.isExtensionAvailable(NORMAL, CameraSelector.LENS_FACING_FRONT);
        Log.d(TAG, "isExtensionAvailable=" + isExtensionAvailable);
     }

    private void initView() {
        mCameraView = findViewById(R.id.view_finder);
        ivBack = findViewById(R.id.iv_back);
        btnZoom = findViewById(R.id.btn_zoom);
        btnFocusing = findViewById(R.id.btn_focusing);
        btnFlashLamp = findViewById(R.id.btn_flash_lamp);
        btnTakePhoto = findViewById(R.id.btn_take_photo);
        btnBrightness = findViewById(R.id.btn_brightness);
        btnFilter = findViewById(R.id.btn_filter);
        btnScene = findViewById(R.id.btn_scene);
        btnCameraDirection = findViewById(R.id.btn_camera_direction);
        llZoom = findViewById(R.id.ll_zoom);
        tvZoomValue = findViewById(R.id.tv_zoom_value);
        seekBarZoom = findViewById(R.id.seek_bar_zoom);
        ivOutPutImage = findViewById(R.id.iv_out_put_image);
        llFlashLamp = findViewById(R.id.ll_flash_lamp);
        radioAuto = findViewById(R.id.radio_flash_lamp_auto);
        radioOff = findViewById(R.id.radio_flash_lamp_off);
        radioOn = findViewById(R.id.radio_flash_lamp_on);
    }

    private void initListener() {
        ivBack.setOnClickListener(this);
        btnZoom.setOnClickListener(this);
        btnFocusing.setOnClickListener(this);
        btnFlashLamp.setOnClickListener(this);
        btnTakePhoto.setOnClickListener(this);
        ivOutPutImage.setOnClickListener(this);
        btnBrightness.setOnClickListener(this);
        btnFilter.setOnClickListener(this);
        btnScene.setOnClickListener(this);
        btnCameraDirection.setOnClickListener(this);
        seekBarZoom.setOnSeekBarChangeListener(seekBarOnChangeListener);
        radioOn.setOnCheckedChangeListener(radioCheckedChangeListener);
        radioOff.setOnCheckedChangeListener(radioCheckedChangeListener);
        radioAuto.setOnCheckedChangeListener(radioCheckedChangeListener);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv_back:
                finish();
                break;
            case R.id.btn_zoom:
                Log.d(TAG, "缩放");
                llZoom.setVisibility(View.VISIBLE);
                llFlashLamp.setVisibility(View.GONE);
                break;
            case R.id.btn_focusing:
                Log.d(TAG, "聚焦");
                llZoom.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.GONE);
                //  preview.focus();
                break;
            case R.id.btn_flash_lamp:
                Log.d(TAG, "闪光灯");
                llZoom.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.VISIBLE);

                break;
            case R.id.btn_take_photo:
                Log.d(TAG, "拍照");
                llZoom.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.GONE);
                if (mCameraView == null) {
                    return;
                }
                //创建图片保存的文件地址
                // File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(), System.currentTimeMillis() + ".jpeg");
                File file = new File(getExternalCacheDir() + "/" + System.currentTimeMillis() + ".jpg");
                mCameraView.setCaptureMode(CameraView.CaptureMode.IMAGE);
                mCameraView.takePicture(new ImageCapture.OutputFileOptions.Builder(file).build(), mExecutorService, new ImageCapture.OnImageSavedCallback() {
                    @Override
                    public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                        if (outputFileResults != null) {
                            Uri savedUri = outputFileResults.getSavedUri();
                            if (savedUri == null) {
                                savedUri = Uri.fromFile(file);
                            }
                            onFileSaved(savedUri);
                            Message message = new Message();
                            message.what = TAKE_PHOTO_SUCCESS;
                            message.obj = file.getAbsolutePath();
                            handler.sendMessage(message);

                        } else {
                            Log.d(TAG, "onImageSaved outputFileResults.getSavedUri()=null");
                        }
                    }

                    @Override
                    public void onError(@NonNull ImageCaptureException exception) {
                        Log.d(TAG, "takePicture onError=" + exception.getImageCaptureError());
                    }
                });
                break;
            case R.id.btn_brightness:
                Log.d(TAG, "亮度");
                llZoom.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.GONE);
                break;
            case R.id.btn_filter:
                Log.d(TAG, "滤镜");
                llZoom.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.GONE);
                break;
            case R.id.btn_scene:
                Log.d(TAG, "场景");
                llZoom.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.GONE);
                break;
            case R.id.btn_camera_direction:
                Log.d(TAG, "方向");
                llZoom.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.GONE);
                if (mCameraView.getCameraLensFacing() == CameraSelector.LENS_FACING_FRONT) {
                    btnCameraDirection.setText("方向：后");
                    mCameraView.setCameraLensFacing(CameraSelector.LENS_FACING_BACK);
                } else {
                    btnCameraDirection.setText("方向：前");
                    mCameraView.setCameraLensFacing(CameraSelector.LENS_FACING_FRONT);
                }
                break;
            case R.id.iv_out_put_image:
                ivOutPutImage.setVisibility(View.GONE);
                llFlashLamp.setVisibility(View.GONE);
                llZoom.setVisibility(View.GONE);
                break;
        }
    }

    private SeekBar.OnSeekBarChangeListener seekBarOnChangeListener = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (seekBarZoom == seekBar) {
                tvZoomValue.setText(String.valueOf(progress) + "X");
                mCameraView.setZoomRatio(progress);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    };

    private CompoundButton.OnCheckedChangeListener radioCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (isChecked) {
                if (radioAuto == buttonView) {
                    btnFlashLamp.setText(ResourceUtil.getString(R.string.auto_flash));
                    mCameraView.setFlash(ImageCapture.FLASH_MODE_AUTO);
                } else if (radioOn == buttonView) {
                    btnFlashLamp.setText(ResourceUtil.getString(R.string.start_flash));
                    mCameraView.setFlash(ImageCapture.FLASH_MODE_ON);
                } else if (radioOff == buttonView) {
                    btnFlashLamp.setText(ResourceUtil.getString(R.string.stop_flash));
                    mCameraView.setFlash(ImageCapture.FLASH_MODE_OFF);
                }
            } else {
            }
        }
    };

    @SuppressLint("RestrictedApi")
    @Override
    protected void onDestroy() {
        mExecutorService.shutdown();
        if(mCameraView!=null){
        }
        super.onDestroy();
    }

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case TAKE_PHOTO_SUCCESS:
                    String filePath = (String) msg.obj;
                    Log.d(TAG, "filePath=" + filePath);
                    ivOutPutImage.setVisibility(View.VISIBLE);
                    Bitmap localBitmap = getLocalBitmap(filePath); //从本地取图片(在cdcard中获取)
                    int degree = 0;
                    if (mCameraView.getCameraLensFacing() == CameraSelector.LENS_FACING_BACK) {
                        degree = readPictureDegree(filePath);
                    } else if (mCameraView.getCameraLensFacing() == CameraSelector.LENS_FACING_FRONT) {
                        degree = 270;
                    }
                    Log.d(TAG, "takePicture degree=" + degree);
                    Bitmap resultBitmap = rotatingImageView(degree, localBitmap);
                    ivOutPutImage.setImageBitmap(resultBitmap); //设置Bitmap
                    saveThumb(new File(filePath), System.currentTimeMillis() + ".jpg");
                    break;
            }
            return false;
        }
    });

    private void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    private File initTakePicPath() {
   /*     String dir = Environment.getExternalStorageDirectory().getPath() + File.separator + "CameraX/";
        File file = new File(dir);
        if (!file.exists()) {
            file.mkdirs();
        }
         String path = dir + System.currentTimeMillis() + ".jpg";*/
        String path = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".jpg";
        return new File(path);
    }

    // 加载本地图片
    private Bitmap getLocalBitmap(String url) {
        try {
            FileInputStream fis = new FileInputStream(url);
            return BitmapFactory.decodeStream(fis);  ///把流转化为Bitmap图片

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 读取图片属性：旋转的角度
     *
     * @param path 图片绝对路径
     * @return degree旋转的角度
     */
    private int readPictureDegree(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    private void onFileSaved(Uri savedUri) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri));
        }
        String mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap
                .getFileExtensionFromUrl(savedUri.getPath()));
        MediaScannerConnection.scanFile(getApplicationContext(),
                new String[]{new File(savedUri.getPath()).getAbsolutePath()},
                new String[]{mimeTypeFromExtension}, new MediaScannerConnection.OnScanCompletedListener() {
                    @Override
                    public void onScanCompleted(String path, Uri uri) {
                        Log.d(TAG, "Image capture scanned into media store: $uri" + uri);
                    }
                });
        // CameraXDemoActivity.start(this, outputFilePath, !takingPicture);
    }

    /*
     * 旋转图片
     * @param angle
     * @param bitmap
     * @return Bitmap
     */
    private Bitmap rotatingImageView(int angle, Bitmap bitmap) {
        //旋转图片 动作
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        // 创建新的图片
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    private void saveThumb(File file, String fileName) {
        // 把文件插入到系统图库
        try {
            MediaStore.Images.Media.insertImage(this.getContentResolver(), file.getAbsolutePath(), fileName, null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 通知图库更新
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + getExternalCacheDir() + "/")));
    }

    /**
     * 转换对焦区域
     * 范围(-1000, -1000, 1000, 1000)
     * x,y是坐标位置，width,height SurfaceView的宽高，coefficient是区域比例大小
     */
    private Rect calculateTapArea(float x, float y, int width, int height, float coefficient) {
        float focusAreaSize = 200;
        //这段代码可以看出coefficient的作用，只是为了扩展areaSize。
        int areaSize = (int) (focusAreaSize * coefficient);
        int surfaceWidth = width;
        int surfaceHeight = height;
        //解释一下为什么*2000，因为要把surfaceView的坐标转换为范围(-1000, -1000, 1000, 1000)，则SurfaceView的中心点坐标会转化为（0,0），x/surfaceWidth ，得到当前x坐标占总宽度的比例，然后乘以2000就换算成了（0,0，2000,2000）的坐标范围内，然后减去1000，就换算为了范围(-1000, -1000, 1000, 1000)的坐标。
        //得到了x,y转换后的坐标，利用areaSize就可以得到聚焦区域。
        int centerX = (int) (x / surfaceHeight * 2000 - 1000);
        int centerY = (int) (y / surfaceWidth * 2000 - 1000);
        int left = clamp(centerX - (areaSize / 2), -1000, 1000);
        int top = clamp(centerY - (areaSize / 2), -1000, 1000);
        int right = clamp(left + areaSize, -1000, 1000);
        int bottom = clamp(top + areaSize, -1000, 1000);
        return new Rect(left, top, right, bottom);
    }

    //不大于最大值，不小于最小值
    private int clamp(int x, int min, int max) {
        if (x > max) {
            return max;
        }
        if (x < min) {
            return min;
        }
        return x;
    }

}